机器翻译数据集

Note

机器翻译指的是将序列从一种语言自动翻译成另一种语言。
本节我们将使用Tatoeba项目的英-法句子对生成机器翻译数据集。

读取数据

import torch
import d2l


#@save
def read_data_nmt():
    """读取英-法数据集"""
    # 读取整个文件
    text = open("../data/fra.txt").read()
    # 使用空格替换不间断空格、大写字母转小写
    text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower()
    # 把标点符号和单词分开
    out = [' ' + char if i > 0 and (char in set(',.!?') and text[i - 1] != ' ') else char 
           for i, char in enumerate(text)]
    return ''.join(out)

Tokenize

#@save
def tokenize_nmt(text, num_examples=None):
    """Tokenize英-法数据集"""
    source, target = [], []
    for i, line in enumerate(text.split('\n')):
        # 保留前num_examples个句子对,可以作为测试集
        if num_examples and i > num_examples:
            break
        # 以\t分隔同一行中的英语和法语
        parts = line.split('\t')
        if len(parts) == 2:
            # tokenize为一个个单词,得到List[List[str]]
            source.append(parts[0].split(' '))
            target.append(parts[1].split(' '))
    return source, target

Tokens转化为数字索引

#@save
def truncate_pad(line, num_steps, padding_token):
    # 通过截断或填充使得每个句子的长度都是num_steps
    if len(line) >= num_steps:
        return line[: num_steps]
    return line + [padding_token] * (num_steps - len(line))
#@save
def build_array_nmt(lines, vocab, num_steps):
    """tokens转化为数字索引,且通过截断或填充使每个句子长度都一样"""
    # <eos>标明句子结束
    lines = [vocab[l] + [vocab['<eos>']] for l in lines]
    # 截断或填充
    array = torch.tensor([truncate_pad(l, num_steps, vocab['<pad>']) for l in lines])
    # 标明哪些token是<pad>
    valid_len = (array != vocab['<pad>']).type(torch.int32).sum(1)
    return array, valid_len

合起来

#@save
def load_data_nmt(batch_size, num_steps, num_examples=600):
    """读取英-法数据集、英语词汇表、法语词汇表"""
    # 读取数据并tokenize
    text = read_data_nmt()
    source, target = tokenize_nmt(text, num_examples)
    # 分别建立英、法词汇表
    src_vocab = d2l.Vocab(source, min_freq=2,
                          reserved_tokens=['<pad>', '<bos>', '<eos>'])
    tgt_vocab = d2l.Vocab(target, min_freq=2,
                          reserved_tokens=['<pad>', '<bos>', '<eos>'])
    # 得到array和valid_len
    src_array, src_valid_len = build_array_nmt(source, src_vocab, num_steps)
    tgt_array, tgt_valid_len = build_array_nmt(target, tgt_vocab, num_steps)
    # 使用load_array建立pytorch-dataset
    data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len)
    data_iter = d2l.load_array(data_arrays, batch_size)
    return data_iter, src_vocab, tgt_vocab
train_iter, src_vocab, tgt_vocab = load_data_nmt(batch_size=2, num_steps=8)
# 试验一下
for X, X_valid_len, Y, Y_valid_len in train_iter:
    print('X:', X.type(torch.int32))
    print('valid lengths for X:', X_valid_len)
    print('Y:', Y.type(torch.int32))
    print('valid lengths for Y:', Y_valid_len)
    break
X: tensor([[ 79,  34,   5,   3,   1,   1,   1,   1],
        [  6, 125,   4,   3,   1,   1,   1,   1]], dtype=torch.int32)
valid lengths for X: tensor([4, 4])
Y: tensor([[  0,   0,   4,   3,   1,   1,   1,   1],
        [  6,  27,   7,   0, 133,   4,   3,   1]], dtype=torch.int32)
valid lengths for Y: tensor([4, 7])